package dao;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.LinkedList;
import java.util.List;
import blogLib.itf.IBlog;
import java.sql.Types;
import model.Blog;
import utils.UtilString;

/**
 * Classe de tradução de dados de objeto para modelo relacional.
 * @author Alan Alves Oliveira
 */
public class BlogDAO extends ConexaoBD {
    /**
     * Ordenação pelo Id.
     */
    public static final int ORDENACAO_ID = 0;
    /**
     * Ordenação pelo título.
     */
    public static final int ORDENACAO_TITULO = 1;
    private Connection conn;

    /**
     * Construtor que gera uma conexão.
     * @throws ClassNotFoundException
     * @throws InstantiationException
     * @throws IllegalAccessException
     * @throws SQLException
     */
    public BlogDAO(String caminho, String nomeBD, String usuario, String senha) throws ClassNotFoundException, InstantiationException, IllegalAccessException, SQLException {
        this.conn = getConexao(caminho, nomeBD, usuario, senha);
    }

    /**
     * Construtor que recebe uma conexão.
     * @param conn Conexão com banco de dados.
     */
    public BlogDAO(Connection conn) {
        this.conn = conn;
    }

    /**
     * Retorna um blog pelo id.
     * @param id Identificador do blog a ser retornado.
     * @return Objeto IBlog. Se o blog não existir, retorna null.
     * @throws SQLException
     */
    public IBlog getBlog(int id) throws SQLException {
        ResultSet rs = null;
        PreparedStatement pst = null;
        IBlog b = null;
        try {
            pst = conn.prepareStatement("SELECT * FROM blog WHERE id = ?");
            pst.setInt(1, id);
            rs = pst.executeQuery();
            if (rs.next())
                b = getBlogFromSQL(rs);
            return b;
        } catch (SQLException e) {
            throw new SQLException(e.getMessage());
        } finally {
            pst.close();
            rs.close();
        }
    }

    /**
     * Retorna uma lista de blogs.
     * @param ordenacao Ordenação da lista de retorno. Deve corresponder aos campos constantes ORDENACAO_ID, ou ORDENACAO_TITULO.
     * @return Objeto List com uma lista de blogs.
     * @throws SQLException
     */
    public List<IBlog> getListaBlog(int ordenacao) throws SQLException {
        List<IBlog> blogs = new LinkedList<IBlog>();
        ResultSet rs = null;
        Statement st = null;
        try {
            st = conn.createStatement();
            rs = st.executeQuery("SELECT * FROM blog" + retornaOrdenacao(ordenacao));
            while (rs.next())
                blogs.add(getBlogFromSQL(rs));
            return blogs;
        } catch (SQLException e) {
            throw new SQLException(e.getMessage());
        } finally {
            st.close();
            rs.close();
        }
    }

    /**
     * Retorna uma lista de blogs filtrando pelo seu usuário proprietário.
     * @param loginUsuario Login do usuário.
     * @param ordenacao Ordenação da lista de retorno. Deve corresponder aos campos constantes ORDENACAO_ID, ou ORDENACAO_TITULO.
     * @return Objeto List com uma lista de blogs.
     * @throws SQLException
     */
    public List<IBlog> getListaBlog(String loginUsuario, int ordenacao) throws SQLException {
        List<IBlog> blogs = new LinkedList<IBlog>();
        ResultSet rs = null;
        PreparedStatement pst = null;
        try {
            pst = conn.prepareStatement("SELECT * FROM blog WHERE UsuarioLogin = ?" + retornaOrdenacao(ordenacao));
            pst.setString(1, loginUsuario);
            rs = pst.executeQuery();
            while (rs.next())
                blogs.add(getBlogFromSQL(rs));
            return blogs;
        } catch (SQLException e) {
            throw new SQLException(e.getMessage());
        } finally {
            pst.close();
            rs.close();
        }
    }

    /**
     * Retorna uma lista de blogs filtrando pela palavra chave.
     * @param palavraChave Palavra chave a ser pesquisada.
     * @param ordenacao Ordenação da lista de retorno. Deve corresponder aos campos constantes ORDENACAO_ID, ou ORDENACAO_TITULO.
     * @return Objeto List com uma lista de blogs.
     * @throws SQLException
     */
    public List<IBlog> getListaBlogPalavraChave(String palavraChave, int ordenacao) throws SQLException {
        List<IBlog> blogs = new LinkedList<IBlog>();
        ResultSet rs = null;
        PreparedStatement pst = null;
        try {
            pst = conn.prepareStatement("SELECT * FROM blog WHERE PalavraChave LIKE ?" + retornaOrdenacao(ordenacao));
            pst.setString(1, "%" + palavraChave + "%");
            rs = pst.executeQuery();
            while (rs.next())
                blogs.add(getBlogFromSQL(rs));
            return blogs;
        } catch (SQLException e) {
            throw new SQLException(e.getMessage());
        } finally {
            pst.close();
            rs.close();
        }
    }

    /**
     * Faz a persistência de um blog no banco de dados.
     * @param b Objeto IBlog.
     * @return Retorna o ID do blog no banco de dados.
     * @throws SQLException
     */
    public int incluiBlog(IBlog b) throws SQLException {
        Statement st = null;
        ResultSet rs = null;
        String sql = "";
        int codigo = -1;
        try {
            sql = "INSERT INTO blog VALUES (" +
                    "0" +
                    ", " + UtilidadeSQL.verificaNulo(b.getLoginUsuario(), Types.VARCHAR) +
                    ", " + UtilidadeSQL.verificaNulo(b.getTitulo(), Types.VARCHAR) +
                    ", " + UtilidadeSQL.verificaNulo(b.getPalavraChave(), Types.VARCHAR) + ")";
            st = conn.createStatement();
            st.executeUpdate(sql, st.RETURN_GENERATED_KEYS);
            rs = st.getGeneratedKeys();
            if (rs.next())
                codigo = rs.getInt(1);
            return codigo;
        } catch (SQLException e) {
            throw new SQLException(e.getMessage());
        } finally {
            st.close();
        }

    }

    /**
     * Altera um blog persistido. Somente os atributos titulo e palavraChave podem ser alterados
     * por este método.
     * @param b Objeto IBlog.
     * @throws SQLException
     */
    public void alteraBlog(IBlog b) throws SQLException {
        PreparedStatement pst = null;
        try {
            pst = conn.prepareStatement("UPDATE blog SET" +
                " Titulo = ?," +
                " PalavraChave = ?" +
                " WHERE Id = ?");
            UtilidadeSQL.verificaNulo(b.getTitulo(), Types.VARCHAR, pst, 1);
            UtilidadeSQL.verificaNulo(b.getPalavraChave(), Types.VARCHAR, pst, 2);
            pst.setInt(3, b.getId());
            pst.execute();
        } catch (SQLException e) {
            throw new SQLException(e.getMessage());
        } finally {
            pst.close();
        }
    }

    /**
     * Exclui um blog persistido.
     * @param id ID do blog.
     * @return Blog excluído. Se o blog não existir, retorna null.
     * @throws SQLException
     */
    public IBlog excluiBlog(int id) throws SQLException {
        PreparedStatement pst = null;
        try {
            IBlog b = this.getBlog(id);
            if (b != null) {
                pst = conn.prepareStatement("DELETE FROM blog WHERE Id = ?");
                pst.setInt(1, id);
                pst.execute();
            }
            return b;
        } catch (SQLException e) {
            throw new SQLException(e.getMessage());
        } finally {
            pst.close();
        }
    }

    /**
     * Instancia um objeto IBlog a partir do resultado em um ResultSet.
     * @param rs Objeto ResultSet que contém os dados do blog.
     * @return Objeto IBlog.
     * @throws SQLException
     */
    private IBlog getBlogFromSQL(ResultSet rs) throws SQLException {
        IBlog b = new Blog();
        b.setId(rs.getInt("Id"));
        b.setLoginUsuario(rs.getString("UsuarioLogin"));
        b.setTitulo(rs.getString("Titulo"));
        b.setPalavraChave(UtilString.isnull(rs.getString("PalavraChave")));
        return b;
    }

        /**
     * Retorna uma string que representa a ordenação em SQL de acordo com a constante
     * ordenacao.
     * @param ordenacao Valor referente à ordenação.
     * @return Retorna uma string com a cláusula ORDER BY da SQL
     */
    private String retornaOrdenacao(int ordenacao) {
        String retorno;
        switch (ordenacao) {
            case ORDENACAO_TITULO:
                retorno = " ORDER BY Titulo";
                break;
            default:
                retorno = " ORDER BY Id";
        }
        return retorno;
    }

}